// Copyright (c) 2011, http://code.google.com/p/example-xplane-plugins/
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of "example-xplane-plugins" nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL "example-xplane-plugins" BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "XPAircraft.h"

#include "XPLMGraphics.h"
#include "XPLMPlanes.h"

#include <string>

namespace
{
  //---------------------
  static const unsigned int MAX_DATAREF_LENGTH(256U);
  //---------------------

  //---------------------
  static const char DEFAULT_AIRCRAFT_VISUALS[] = {"Some aircraft acf file"};
  //---------------------

  //---------------------
  static const char DATAREF_AIRCRAFT_OVERRIDE[] = {"sim/operation/override/override_planepath"};
  //---------------------

  //---------------------
  static const char DATAREF_AIRCRAFT_USER_X[] = {"sim/flightmodel/position/local_x"};
  static const char DATAREF_AIRCRAFT_USER_Y[] = {"sim/flightmodel/position/local_y"};
  static const char DATAREF_AIRCRAFT_USER_Z[] = {"sim/flightmodel/position/local_z"};

  static const char DATAREF_AIRCRAFT_USER_H[] = {"sim/flightmodel/position/psi"};
  static const char DATAREF_AIRCRAFT_USER_P[] = {"sim/flightmodel/position/theta"};
  static const char DATAREF_AIRCRAFT_USER_R[] = {"sim/flightmodel/position/phi"};
  //---------------------

  // We will use sprintf to replace the %i with a number!
  //---------------------
  static const char DATAREF_AIRCRAFT_MULTI_X[] = {"sim/multiplayer/position/plane%i_x"};
  static const char DATAREF_AIRCRAFT_MULTI_Y[] = {"sim/multiplayer/position/plane%i_y"};
  static const char DATAREF_AIRCRAFT_MULTI_Z[] = {"sim/multiplayer/position/plane%i_z"};

  static const char DATAREF_AIRCRAFT_MULTI_H[] = {"sim/multiplayer/position/plane%i_psi"};
  static const char DATAREF_AIRCRAFT_MULTI_P[] = {"sim/multiplayer/position/plane%i_the"};
  static const char DATAREF_AIRCRAFT_MULTI_R[] = {"sim/multiplayer/position/plane%i_phi"};
  //---------------------
};


//-----------------------------------------------------------------------------
// CONSTRUCTION/DESTRUCTION
//-----------------------------------------------------------------------------
XPAircraft::XPAircraft():
m_overrideXPlane(false),
m_aircraftIndex(UNBOUND_AIRCRAFT_INDEX),
m_aircraftXPosID(0),
m_aircraftYPosID(0),
m_aircraftZPosID(0),
m_aircraftHeadingDegID(0),
m_aircraftPitchDegID(0),
m_aircraftRollDegID(0)
{
  m_aircraftOverrideID = XPLMFindDataRef(DATAREF_AIRCRAFT_OVERRIDE);
}

XPAircraft::~XPAircraft()
{
  // Destruct
}
//-----------------------------------------------------------------------

//-----------------------------------------------------------------------------
// STATIC MEMBERS AND FUNCTIONS
//-----------------------------------------------------------------------------
int XPAircraft::s_overrideFlags[MAX_AIRCRAFT] = {0};

int XPAircraft::s_totalAcquiredAircraft(0U);
char XPAircraft::s_filenames[MAX_AIRCRAFT][MAX_PATH] = {0};
char XPAircraft::s_filepaths[MAX_AIRCRAFT][MAX_PATH] = {0};
char* XPAircraft::s_inAircraft[MAX_AIRCRAFT] = {0};

void XPAircraft::ReleaseAllAircraft()
{
  XPLMReleasePlanes();
  
  s_totalAcquiredAircraft = 0;
}

int XPAircraft::AcquireAllAircraft()
{
  char allACFfilenames[MAX_AIRCRAFT][MAX_PATH] = {0};
  char ownACFfilename[MAX_PATH] = {0};
  char ownACFfilepath[MAX_PATH] = {0};
  char *inAircraft[9] = {0};

  memset(s_filepaths, 0, sizeof(s_filepaths));
  s_totalAcquiredAircraft=0;

  XPLMGetNthAircraftModel(0, ownACFfilename, ownACFfilepath);
  XPLMCountAircraft(&s_totalAcquiredAircraft, 0, 0);

  if (s_totalAcquiredAircraft>0)
  {
    for (int n=0; n<s_totalAcquiredAircraft; ++n)
    {
      strcpy(allACFfilenames[n], ownACFfilepath);
      inAircraft[n] = allACFfilenames[n];
    }

    if (XPLMAcquirePlanes(inAircraft,0,0))
    {
      for (int n = 1; n < s_totalAcquiredAircraft; n++)
      {
       // XPLMSetAircraftModel(n, inAircraft[n]);
       // XPLMGetNthAircraftModel(n, s_filenames[n], s_filepaths[n]);
      }
    }
  }

  return s_totalAcquiredAircraft;
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// MEMBER FUNCTIONS - XPLANE ADMINISTRATION
//-----------------------------------------------------------------------------
void XPAircraft::SetOverrideXPlaneState(const bool overrideXPlane)
{
  // Save this objects state
  m_overrideXPlane = overrideXPlane;

  // Update shared override array - not thread safe
  s_overrideFlags[m_aircraftIndex] = m_overrideXPlane;

  // Update current XPlane Flight model overrides works for ALL aircraft
  XPLMSetDatavi(m_aircraftOverrideID, s_overrideFlags, 0, MAX_AIRCRAFT);

  if ((m_aircraftIndex>=MIN_MULTI_AIRCRAFT_INDEX)&&(m_aircraftIndex<=MAX_MULTI_AIRCRAFT_INDEX))
  {
    XPLMDisableAIForPlane(m_aircraftIndex);
  }
}


void XPAircraft::BindToXPlaneAircraft(const int aircraftIndex)
{
  m_aircraftIndex = aircraftIndex;

  InitialiseDatarefs();
}

void XPAircraft::InitialiseDatarefs()
{
  // Get dataref handles
  if (USER_AIRCRAFT_INDEX==m_aircraftIndex)
  {
    m_aircraftXPosID = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_X);
    m_aircraftYPosID = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_Y);
    m_aircraftZPosID = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_Z);

    m_aircraftHeadingDegID = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_H);
    m_aircraftPitchDegID   = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_P);
    m_aircraftRollDegID    = XPLMFindDataRef(DATAREF_AIRCRAFT_USER_R);
  }
  else if ((m_aircraftIndex>=MIN_MULTI_AIRCRAFT_INDEX)&&(m_aircraftIndex<=MAX_MULTI_AIRCRAFT_INDEX))
  {
    char buffer[MAX_DATAREF_LENGTH] = {0};

    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_X, m_aircraftIndex);
    m_aircraftXPosID = XPLMFindDataRef(buffer);
    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_Y, m_aircraftIndex);
    m_aircraftYPosID = XPLMFindDataRef(buffer);
    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_Z, m_aircraftIndex);
    m_aircraftZPosID = XPLMFindDataRef(buffer);

    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_H, m_aircraftIndex);
    m_aircraftHeadingDegID = XPLMFindDataRef(buffer);
    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_P, m_aircraftIndex);
    m_aircraftPitchDegID = XPLMFindDataRef(buffer);
    sprintf(buffer, DATAREF_AIRCRAFT_MULTI_R, m_aircraftIndex);
    m_aircraftRollDegID = XPLMFindDataRef(buffer);
  }
  else if (UNBOUND_AIRCRAFT_INDEX==m_aircraftIndex)
  {
    m_aircraftXPosID = 0;
    m_aircraftYPosID = 0;
    m_aircraftZPosID = 0;

    m_aircraftHeadingDegID = 0;
    m_aircraftPitchDegID   = 0;
    m_aircraftRollDegID    = 0;
  }
  else
  {
    // ERROR
  }
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// MEMBER FUNCTIONS - AIRCRAFT <-> XPLANE COMMS
//-----------------------------------------------------------------------------
void XPAircraft::SetXPlaneAircraft_LLA(const double latitudeDeg, const double longitudeDeg, const double altitudeM,
                                       const float headingDeg, const float pitchDeg, const float rollDeg)
{
  // Get XYZ
  double X(0.0);
  double Y(0.0);
  double Z(0.0);

  XPLMWorldToLocal(latitudeDeg, longitudeDeg, altitudeM, &X, &Y, &Z);

  SetXPlaneAircraft_XYZ(X, Y, Z, headingDeg, pitchDeg, rollDeg);
}

void XPAircraft::SetXPlaneAircraft_XYZ(double X, double Y, double Z,
                                       float headingDeg, float pitchDeg, float rollDeg)
{
  XPLMSetDatad(m_aircraftXPosID, X);
  XPLMSetDatad(m_aircraftYPosID, Y);
  XPLMSetDatad(m_aircraftZPosID, Z);

  XPLMSetDataf(m_aircraftHeadingDegID, headingDeg);
  XPLMSetDataf(m_aircraftPitchDegID,   pitchDeg);
  XPLMSetDataf(m_aircraftRollDegID,    rollDeg);
}

void XPAircraft::GetXPlaneAircraft_LLA(double& latitudeDeg, double& longitudeDeg, double& altitudeM,
                                       float& headingDeg, float& pitchDeg, float& rollDeg)
{
  // Get XYZ
  double X(0.0);
  double Y(0.0);
  double Z(0.0);

  GetXPlaneAircraft_XYZ(X, Y, Z, headingDeg, pitchDeg, rollDeg);

  XPLMLocalToWorld(X, Y, Z, &latitudeDeg, &longitudeDeg, &altitudeM);
}

void XPAircraft::GetXPlaneAircraft_XYZ(double& X, double& Y, double& Z,
                                       float& headingDeg, float& pitchDeg, float& rollDeg)
{
  X = XPLMGetDatad(m_aircraftXPosID);
  Y = XPLMGetDatad(m_aircraftYPosID);
  Z = XPLMGetDatad(m_aircraftZPosID);

  headingDeg = XPLMGetDataf(m_aircraftHeadingDegID);
  pitchDeg   = XPLMGetDataf(m_aircraftPitchDegID);
  rollDeg    = XPLMGetDataf(m_aircraftRollDegID);
}
